/****************************************************************************
 *
 *   Copyright (c) 2019 PX4 Development Team. All rights reserved.
 *   Copyright (c) 2025 H743 Flight Controller Project. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#pragma once

#include "work_queue_manager.hpp"
#include "work_queue.hpp"

#include <px4_platform_common/intrusive_queue.h>
#include <px4_platform_common/containers/intrusive_sorted_list.h>
#include <px4_platform_common/hrt_call.h>
#include <px4_platform_common/atomic.hpp>

#include <string.h>
#include <stdint.h>

namespace px4
{

/**
 * WorkItem - Base class for work items that can be scheduled on work queues
 *
 * This class provides the foundation for all work items in the PX4 work queue system.
 * Work items are scheduled to run on specific work queues and can be:
 * - Scheduled immediately (ScheduleNow)
 * - Scheduled with a delay (ScheduledWorkItem)
 * - Scheduled periodically (ScheduledWorkItem)
 *
 * Work items are sorted by name in the work queue's attached list, and queued
 * in FIFO order in the runnable queue.
 *
 * Usage:
 * @code
 * class MyWorkItem : public px4::WorkItem
 * {
 * public:
 *     MyWorkItem() : WorkItem("my_work", px4::wq_configurations::hp_default) {}
 * private:
 *     void Run() override {
 *         // Do work here
 *         ScheduleNow(); // Reschedule if needed
 *     }
 * };
 * @endcode
 */
class WorkItem
{
public:
	WorkItem() = delete;

	// No copy, assignment, move, move assignment
	WorkItem(const WorkItem &) = delete;
	WorkItem &operator=(const WorkItem &) = delete;
	WorkItem(WorkItem &&) = delete;
	WorkItem &operator=(WorkItem &&) = delete;

	/**
	 * Schedule this work item to run immediately on its work queue
	 */
	inline void ScheduleNow()
	{
		if (_wq != nullptr) {
			_should_run = true; // Reset cancel flag
			_wq->Add(this);
		}
	}

	/**
	 * Remove work item from the runnable queue, if it's there
	 */
	void ScheduleClear();

	/**
	 * Print run status (rate, interval, etc.)
	 */
	virtual void print_run_status();

	/**
	 * Switch to a different WorkQueue.
	 * NOTE: Caller is responsible for synchronization.
	 *
	 * @param config The WorkQueue configuration (see work_queue_manager.hpp).
	 * @return true if initialization was successful
	 */
	bool ChangeWorkQueue(const wq_config_t &config) { return Init(config); }

	/**
	 * Get the name of this work item
	 */
	const char *ItemName() const { return _item_name; }

	/**
	 * Get elapsed time since first run (in seconds)
	 */
	float elapsed_time() const;

	/**
	 * Get average run rate (Hz)
	 */
	float average_rate() const;

	/**
	 * Get average interval between runs (microseconds)
	 */
	float average_interval() const;

	// Intrusive list node for sorted list (attached work items)
	intrusive_sorted_list_node_t sorted_list_node;

	// Intrusive queue node for runnable queue
	intrusive_queue_node_t queue_node;

	// Comparison operator for sorted list (sorted by name)
	static bool compare_by_name(const void *a, const void *b)
	{
		const WorkItem *item_a = (const WorkItem *)a;
		const WorkItem *item_b = (const WorkItem *)b;
		return strcmp(item_a->ItemName(), item_b->ItemName()) <= 0;
	}

	// Statistics (public for testing)
	hrt_abstime _time_first_run{0};  ///< Time of first run (for statistics)
	const char *_item_name;           ///< Name of this work item
	uint32_t _run_count{0};           ///< Number of times Run() has been called
	atomic_bool _should_run{true};    ///< Atomic flag to control execution (for ScheduleClear)

protected:
	/**
	 * Constructor
	 * @param name Name of this work item (for debugging/status)
	 * @param config Work queue configuration
	 */
	explicit WorkItem(const char *name, const wq_config_t &config);

	/**
	 * Copy constructor (copies work queue from another work item)
	 * @param name Name of this work item
	 * @param work_item Work item to copy work queue from
	 */
	explicit WorkItem(const char *name, const WorkItem &work_item);

	/**
	 * Destructor
	 */
	virtual ~WorkItem();

	/**
	 * Run preamble - called before Run()
	 * Updates statistics (run count, first run time)
	 * @return true if should run, false if cancelled
	 */
	bool RunPreamble()
	{
		// Check if cancelled
		if (!_should_run) {
			_should_run = true; // Reset for next schedule
			return false;
		}

		if (_run_count == 0) {
			_time_first_run = hrt_absolute_time();
			_run_count = 1;
		} else {
			_run_count++;
		}
		return true;
	}

	/**
	 * The work item's main function - must be implemented by derived classes
	 * This is called by the work queue when the item is scheduled to run.
	 */
	virtual void Run() = 0;

	/**
	 * Initialize WorkItem given a WorkQueue config. This call
	 * can also be used to switch to a different WorkQueue.
	 * NOTE: Caller is responsible for synchronization.
	 *
	 * @param config The WorkQueue configuration (see work_queue_manager.hpp).
	 * @return true if initialization was successful
	 */
	bool Init(const wq_config_t &config);

	/**
	 * Deinitialize - detach from work queue
	 */
	void Deinit();

private:
	WorkQueue *_wq{nullptr};          ///< Work queue this item is attached to

	// WorkQueue needs access to Run() and RunPreamble()
	friend class WorkQueue;
};

} // namespace px4

